package org.zfes.snowier.cloud.zbss.asf.biz.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PostConstruct;
import com.arcsoft.face.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.zfes.snowier.cloud.zbss.asf.biz.cache.IUserDataSet;
import org.zfes.snowier.cloud.zbss.asf.biz.dto.FaceUserResDto;
import org.zfes.snowier.cloud.zbss.asf.biz.dto.ImageInfo;
import org.zfes.snowier.cloud.zbss.asf.biz.dto.ProcessResDto;
import org.zfes.snowier.cloud.zbss.asf.biz.model.UserFaceInfo;
import org.zfes.snowier.cloud.zbss.asf.biz.service.IFaceEngineService;
import org.zfes.snowier.cloud.zbss.asf.engine.FaceEngineContainer;
import org.zfes.snowier.core.util.ZAlert;
import org.zfes.snowier.core.util.ZAssert;
import org.zfes.snowier.core.util.ZBeanUtil;
import org.zfes.snowier.core.util.ZObjectUtil;
import com.arcsoft.face.enums.ImageFormat;
import com.google.common.collect.Lists;


@Service
public class FaceEngineServiceImpl implements IFaceEngineService{
	
	 public final static Logger logger = LoggerFactory.getLogger(FaceEngineServiceImpl.class);
	 
	   
    @Value("${asf.appid}")
    public String appId;

    @Value("${asf.sdkkey}")
    public String sdkKey;

    @Value("${asf.thread-pool-size}")
    public Integer threadPoolSize;
    
    @Value("${asf.max-engine-size}")
    public Integer maxEngineSize;
    
    @Value("${asf.partition-size}")
    public Integer partitionSize=100;
    
    @Value("${asf.pass-rate}")
    private Float passRate = 0.80f;
    
    private ExecutorService executorService;
    
    @Autowired
    private  IUserDataSet userDataSet;

    
    private FaceEngineContainer container;
    
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@PostConstruct
    public  void init() {
        executorService = Executors.newFixedThreadPool(threadPoolSize);
        container=FaceEngineContainer.getInstance(appId,sdkKey,maxEngineSize);
    }
    
    @Override
    public List<FaceInfo> detectFaces(ImageInfo imageInfo){
    	List<FaceInfo> faceInfoList =Lists.newArrayList();
        FaceEngine faceEngine = null;
        try {
            //获取引擎对象
            faceEngine = container.take().get();
            //人脸检测
            faceEngine.detectFaces(imageInfo.getRgbData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList);
           if(faceInfoList.size()>1) {
        	   ZAlert.serviceLogicalError("检测到多于一个人脸，请重试");
           }
            return faceInfoList;
        } finally {
        	container.add(faceEngine);

        }
    }
	
    @Override
    public   byte[] extractFaceFeature(ImageInfo imageInfo,List<FaceInfo> faceInfoList) {
    	  FaceEngine faceEngine = null;
    	  ZAssert.notEmpty(faceInfoList, "未检测到人脸数据");
          try {
              //获取引擎对象
              faceEngine = container.take().get();
              FaceFeature faceFeature = new FaceFeature();
               //提取人脸特征
              int res= faceEngine.extractFaceFeature(imageInfo.getRgbData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList.get(0), faceFeature);
              logger.info("extractFaceFeature-extractFaceFeature:"+res);
              
              ZAssert.equals(0, res, "提取人脸特征失败，错误码："+res);
             
              return faceFeature.getFeatureData();
            
          } finally {
        	  container.add(faceEngine);
          }
    }

    @Override
    public ProcessResDto process(ImageInfo imageInfo, List<FaceInfo> faceInfoList) {
        FaceEngine faceEngine = null;
        ZAssert.notEmpty(faceInfoList, "未检测到人脸数据");
        if(faceInfoList.size()>1) {
        	ZAlert.throwSLE("检测到多余一张人脸");
        }
        
        try {
        	
        	faceEngine = container.take().get();
           
            int processResult = faceEngine.process(imageInfo.getRgbData(), imageInfo.getWidth(), imageInfo.getHeight(), 
            		ImageFormat.CP_PAF_BGR24, faceInfoList, 
            		FunctionConfiguration.builder()
            		.supportAge(true)
            		.supportFace3dAngle(true)
            		.supportGender(true)
            		.supportLiveness(true).build());
            
            ZAssert.equals(Integer.valueOf("0"),processResult,"活体检测失败错误码："+processResult);

        
            List<GenderInfo> genderInfoList = new ArrayList<GenderInfo>();  //性别提取
            List<AgeInfo> ageInfoList = new ArrayList<AgeInfo>();//年龄提取
            
            int genderCode = faceEngine.getGender(genderInfoList);
            int ageCode = faceEngine.getAge(ageInfoList);
            
            ZAssert.equals(Integer.valueOf("0"),genderCode,"性别提取失败错误码："+genderCode);
            ZAssert.equals(Integer.valueOf("0"),ageCode,"年龄提取失败错误码："+genderCode);
            
            if(genderInfoList.isEmpty()) {
            	ZAlert.throwSLE("性别提取失败");
            }
            if(ageInfoList.isEmpty()) {
            	ZAlert.throwSLE("性别提取失败");
            }
            
            int gender = genderInfoList.get(0).getGender();
            int age=  ageInfoList.get(0).getAge();
            
            
            List<LivenessInfo> livenessInfoList =Lists.newArrayList();
            
            int livenessCode = faceEngine.getLiveness(livenessInfoList);

            //0：非真人；1：真人；-1：不确定；-2:传入人脸数>1；
            ZAssert.equals(Integer.valueOf("0"),processResult,"活体检测失败错误码："+livenessCode);
            
            int liveness =  livenessInfoList.get(0).getLiveness();

            ZAssert.equals(Integer.valueOf("1"),liveness,"检测到当前识别图像不是真人，请重试");

            return new ProcessResDto( gender,  age,  liveness);
        }  finally {
            container.add(faceEngine);
        }
    }
    
    
    @Override
    public  List<FaceUserResDto> compareFaceFeature(byte[] faceFeature){
    	
    	List<FaceUserResDto> resultFaceInfoList = Lists.newLinkedList();//识别到的人脸列表
    	
        FaceFeature targetFaceFeature = new FaceFeature(faceFeature);
        
        //分段，每段一个线程--执行遍历compareFaceFeature比较
    	List<List<UserFaceInfo>> userInfoPartList =	Lists.partition(userDataSet.getData(), partitionSize);
    	 logger.info("compareFaceFeature-userInfoPartList-size:"+userInfoPartList.size()+"||userDataSet.getData()-size:"+userDataSet.getData().size());
    	//CompletionService 线程池用于收集compareFaceFeature结果。ExecutorService 没有队列需要手动循环等待，所以此处用CompletionService、
    	//注意CompletionService采用阻塞队列存储结果，要执行completionService.take()。消费掉队列数据，防止占用系统内存
    	 CompletionService<List<FaceUserResDto>> completionService = new ExecutorCompletionService<List<FaceUserResDto>>(executorService);
    	 
    	 //分段循环
    	  for (List<UserFaceInfo> partUserList : userInfoPartList) {
    		  
              completionService.submit(()->{
            	  
            		//本段内识别到的人脸列表
                    List<FaceUserResDto> partResultFaceInfoList = Lists.newLinkedList();//
                    FaceEngine  faceEngine = null;//GenericObjectPool 需要在final内归还
                    try {
                    	
                    	faceEngine =  container.take().get();
                        
                        for (UserFaceInfo userInfo : partUserList) {
                        	
                        	//userInfo：getFaceFeature 数据库中的特征值
                            FaceFeature sourceFaceFeature = new FaceFeature();
                            sourceFaceFeature.setFeatureData(userInfo.getFaceFeature());
                            
                           //特征对比
                            FaceSimilar faceSimilar = new FaceSimilar();
                            int a=faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
                            
                            logger.info("compareFaceFeature-faceEngine.compareFaceFeature:"+a);
                            
                            float similarValue = faceSimilar.getScore();//获取相似值
                           
                            logger.info("compareFaceFeature-faceEngine.compareFaceFeature-similarValue:"+similarValue);
                           
                            if (similarValue > passRate) {//相似值大于配置预期，加入到识别到人脸的列表
                            	//返回copy版本的UserFaceInfo
                            	UserFaceInfo target  =new UserFaceInfo(userInfo).setFaceFeature(null);
                                partResultFaceInfoList.add(new FaceUserResDto(target,similarValue));
                                
                                break;//提早结束
                            }
                        }
                    } catch (Exception e) {
                    	e.printStackTrace();
                        logger.error("", e);
                    } finally {
                    	 container.add(faceEngine);
                    }
                    return partResultFaceInfoList;
            	});
    		  
    		  
          }
    	  
    	  userInfoPartList.forEach(val->{
    		  
    		  try {
    				
    			  List<FaceUserResDto> faceUserInfoPartList =  completionService.take().get();
    			  
    			  if (ZObjectUtil.isNotEmpty(faceUserInfoPartList)) {
    				  
                      resultFaceInfoList.addAll(faceUserInfoPartList);
                  }
    			  
  			} catch (InterruptedException | ExecutionException e) {
  				e.printStackTrace();
  			}
    	  });
    	

          resultFaceInfoList.sort((h1, h2) -> h2.getSimilarScore().compareTo(h1.getSimilarScore()));//从大到小排序
          
          return resultFaceInfoList;
    }




//    @Override
//    public  List<ProcessInfo> process(ImageInfo imageInfo){
//    	  FaceEngine faceEngine = null;
//    	  List<ProcessInfo> processInfoList=Lists.newLinkedList();
//          try {
//              //获取引擎对象
//              faceEngine = extractFaceObjectPool.borrowObject();
//              //人脸检测得到人脸列表
//              List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
//              //人脸检测
//              faceEngine.detectFaces(imageInfo.getRgbData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList);
//              int processResult = faceEngine.process(imageInfo.getRgbData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList, FunctionConfiguration.builder().supportAge(true).supportGender(true).build());
//              logger.info("process-processResult:"+processResult);
//
//              List<GenderInfo> genderInfoList = new ArrayList<GenderInfo>();
//              //性别提取
//              int genderCode = faceEngine.getGender(genderInfoList);
//              logger.info("process-genderCode:"+genderCode);
//              //年龄提取
//              List<AgeInfo> ageInfoList = new ArrayList<AgeInfo>();
//              int ageCode = faceEngine.getAge(ageInfoList);
//              logger.info("process-ageCode:"+ageCode);
//              for (int i = 0; i <genderInfoList.size() ; i++) {
//                  ProcessInfo processInfo=new ProcessInfo();
//                  processInfo.setGender(genderInfoList.get(i).getGender());
//                  processInfo.setAge(ageInfoList.get(i).getAge());
//                  processInfoList.add(processInfo);
//              }
//              return processInfoList;
//
//          } catch (Exception e) {
//              logger.error("", e);
//          } finally {
//              if (faceEngine != null) {
//                  //释放引擎对象
//                  extractFaceObjectPool.returnObject(faceEngine);
//              }
//          }
//
//          return processInfoList;
//    }







}
